/* === LICENSE INFO ===
 * This source file is subject to the GPLv3 license that is bundled with this
 * package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl.txt
 * === end of LICENSE INFO === */

/*==============================================================================
  File Name   : GLog.hpp
  Description : Declaration for GLog module of gUtil.

  Created on  : 2009-07-26
  Updated on  : 2011-01-30
  Updated by  : Lee Shiou Ming

  Cautions    : (important notes)
  ============================================================================*/

#ifndef GLOG_HPP_
#define GLOG_HPP_

#include "GLogDefinition.hpp"
#include "LogConfig.hpp"
#include "LogWriterManager.hpp"
#include <boost/scoped_ptr.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/thread.hpp>
#include <string>
#include <vector>


/**
 * @brief All gUtil modules will be wrapped within gUtil namespace.
 */
namespace gUtil
{
/**
 * @brief Namespace for GLog module.
 */
namespace GLog
{
    /**
     * @brief A singleton class that provides logging facilities.
     *
     * IMPORTANT NOTE:
     * - All public static interfaces of GLog may throw runtime exception if the
     *   logger instance is no more valid or already destroyed.
     * - To use those public interfaces for configuring logger, client code has
     *   to call these functions BEFORE ANY invocation of getInstance(),
     *   otherwise those custom setting will NOT take effect.
     * - Client code MUST call initConfig() after finish configure the logger.
     *   Otherwise custom setting will be overwrite by default setting on first
     *   call to getInstance().
     */
    class GLog
    {
    public:
        //=== Public interfaces for configuring logger
        /**
         * @brief Only log entries with this severity level or below will be
         *        logged; The rest will be discarded.
         *
         * @param _maxLogLevel
         * [IN] Log level that client wants to set as max.
         *
         * @return
         * - true ==> Succeed.
         * - false ==> Logger is already in configured state.
         *
         * @throw runtime_error if instance already deleted.
         */
        static bool setMaxLogLevel(const LogLevel _maxLogLevel);

        /**
         * @brief For client to specify the type of log output that needs to be
         *        enabled.
         *
         * @param _enabledOutput
         * [IN] Output type that client wants to enable.
         *
         * @return
         * - true ==> Succeed.
         * - false ==> Logger is already in configured state.
         *
         * @throw runtime_error if instance already deleted.
         */
        static bool addOutputType(const LogOutputType _enabledOutput);

        /**
         * @brief Setter for log file path. Default value is "GLog".
         *
         * Will be discarded if logger is already in configured state.
         *
         * @param _logFilePath
         * [IN] Log file path that client wants to use.
         *
         * @return
         * - true ==> Succeed.
         * - false ==> Logger is already in configured state.
         *
         * @throw runtime_error if instance already deleted.
         */
        static bool setLogFilePath(const std::string& _logFilePath);

        /**
         * @brief Setter for log file rotation size.
         *
         * Will be discarded if logger is already in configured state. If
         * _logRotationSize is less than 1, actual rotation size will be set to
         * DEFAULT_LOG_ROTATION_SIZE_BYTE (about 1GB). If _logRotationSize is
         * greater than MAX_LOG_FILE_SIZE_BYTE, the size will be set to max
         * instead (about 4GB). Log message larger than rotation size will not
         * be written into log file.
         *
         * @param _logRotationSize
         * [IN] Log file rotation size in byte.
         *
         * @return
         * - true ==> Succeed.
         * - false ==> Logger is already in configured state.
         *
         * @throw runtime_error if instance already deleted.
         */
        static bool setLogRotationSize(unsigned int _logRotationSize);

        /**
         * @brief Setter for log file rotation interval.
         *
         * Will be discarded if logger is already in configured state.
         *
         * @param _logRotationInterval
         * [IN] Log file rotation interval.
         *
         * @return
         * - true ==> Succeed.
         * - false ==> Logger is already in configured state.
         *
         * @throw runtime_error if instance already deleted.
         */
        static bool setLogRotationInterval(const TimeInterval _logRotationInterval);

        /**
         * @brief Initialize and apply configuration that has been set.
         *
         * Call this after configuration BEFORE any invocation to getInstance(),
         * otherwise logger will be auto initialized with default setting.
         *
         * @return
         * - true ==> Succeed.
         * - false ==> Logger is already in configured state.
         *
         * @throw runtime_error if instance already deleted.
         */
        static bool initConfig();

        /**
         * @brief Load and use logger setting from configuration file.
         *
         * @param _configFile
         * [IN] Path to the logger's configuration file.
         * @param _configFileFormat
         * [IN] File format of _configFile.
         *
         * @return
         * - true ==> Setting is loaded from configuration file and applied on
         *            logger successfully.
         * - false ==> Either encountered error while loading log setting from
         *             configuration file, or logger is already in configured
         *             state. No changes were made to the logger's current state.
         *
         * @throw runtime_error if instance already deleted.
         */
        static bool loadFromConfigFile(const std::string& _configFile,
                const ConfigFileFormat _configFileFormat);
        //=== end of public interfaces for configuring logger

        /**
         * @brief Getter for log file path.
         *
         * @return current log file path.
         *
         * @throw runtime_error if instance already deleted.
         */
        static std::string getLogFilePath();

        /**
         * @brief Getter for logger's configured state.
         *
         * @return
         * - true ==> Logger is in configured state.
         * - false ==> Not in configured state.
         *
         * @throw runtime_error if instance already deleted.
         */
        static bool getConfigState();

        /**
         * @brief Getter for max log level.
         *
         * @return Max log level that currently set to logger.
         *
         * @throw runtime_error if instance already deleted.
         */
        static LogLevel getMaxLogLevel();

        /**
         * @brief Tells client if the specified output type is in enabled list.
         *
         * @param _outputType
         * [IN] Log output type that caller wants to query.
         *
         * @return
         * - true ==> Already enabled.
         * - false ==> Not in the list of enabled output type.
         *
         * @throw runtime_error if instance already deleted.
         */
        static bool checkOutputEnabledState(const LogOutputType _outputType);

        /**
         * @brief For client code to obtain a reference to the logger instance.
         *
         * Take note that on first call to this function, logger instance will
         * be initialized with default setting if client code had not configure
         * it yet.
         *
         * @return Reference to the only GLog instance.
         *
         * @throw runtime_error if instance already deleted.
         */
        static GLog& getInstance();

        /**
         * @brief For client code to append a log message with specified log
         *        severity level.
         *
         * @param _logMessage
         * [IN] The message which log client wants to append.
         * @param _logLevel
         * [IN] Severity level of this log message.
         *
         * @return
         * - true ==> Succeed.
         * - false ==> Either encounter error while logging the message, or the
         *             message has been filtered out and discarded by log
         *             filter.
         */
        bool append(const std::string _logMessage,
                const LogLevel _logLevel);

        /**
         * @brief An empty destructor.
         *
         * Declare destructor as public scoped so that smart pointer of private
         * member is able to delete the instance automatically, yet client code
         * will not accidentally delete it as long as there is no public
         * interface that allows client code directly access the pointer.
         */
        ~GLog() {}

    private:
        /**
         * @brief Private constructor performs member initialization.
         */
        GLog();
        GLog(GLog const&);
        GLog& operator=(GLog const&);

        /**
         * @brief All public functions will internally invoke this, prevent
         *        referencing to destroyed logger instance.
         *
         * @throw runtime_error if instance already deleted.
         */
        static void validateObjectAccess();

        /**
         * @brief Use default configuration.
         */
        void initDefaultConfig();

        /**
         * @brief Create and initialize log writers for those enabled output
         *        types.
         *
         * @return
         * - true ==> Succeed.
         * - false ==> None of the log output types is enabled, hence default
         *             output type will be enabled automatically.
         */
        bool initLogWriters();

        /**
         * @brief Check if this log message has a log level lower than or equal
         *        to the max level.
         *
         * @param _logLevel
         * [IN] Log level of a particular log message entry.
         *
         * @return
         * - true ==> Passed the filtering.
         * - false ==> This log message entry should be discarded.
         */
        bool filterByLogLevel(const LogLevel _logLevel);

        /**
         * @brief Pointing to the only logger instance
         */
        static boost::scoped_ptr<GLog> sm_uniqueInstance;

        /**
         * @brief Mutex to sync access to logger configuration from multiple
         *        threads.
         */
        static boost::recursive_mutex sm_configMutex;

        /**
         * @brief Mutex to prevent concurrent logger deletion from multiple
         *        threads.
         */
        static boost::mutex sm_loggerDeletionMutex;

        /**
         * @brief Member object that manages all the log writers on behalf of
         *        GLog.
         */
        LogWriterManager m_writerManager;

        /**
         * @brief Logger's configuration is pushed into this member object.
         */
        LogConfig m_logConfig;

    };  /* end of class GLog */

}  /* end of namespace GLog */
}  /* end of namespace gUtil */

#endif  /* GLOG_HPP_ */
